package de.lns.tick;

import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;

import de.lns.Player;
import de.lns.render.Renderer;

public class UpdateThread extends Thread {

	/**
	 * An instance of UpdateThread,
	 * that is used for this session.
	 */
	public static final UpdateThread instance;
	/**
	 * Put an instance of UpdateThread into instance.
	 * 
	 * This is a static code block and
	 * will only be executed the first time,
	 * this class is accessed in any way.
	 */
	static{
		instance = new UpdateThread();
	}
	
	/**
	 * Make the constructor invisible.
	 * This class relies on the singleton-pattern
	 * and only ever has one instance.
	 */
	private UpdateThread(){
		super("Main Loop");
	}
	
	//Main part of the class starts here//
	
	/**The time(in milliseconds) of the last frame.*/
	private static long lastFrame = 0;
	/**The time(in milliseconds) of the current frame.*/
	private static long currentFrame = 0;
	/**The time(in milliseconds) since the last frame.*/
	private static int delta = 0;
	
	/**The frame rate cap (no cap if 0).*/
	private static int frameCap = 60;
	
	
	@Override
	/**
	 * This is the main game loop.
	 * It gets called after the startup 
	 * has set up all important resources.
	 */
	public void run() {
		try {
			calcDelta();
			calcDelta();
			/* TODO: Call all necessary setup functions.
			 * (This will not always be the case during testing)
			 */
			//Set up the rendering engine.
			Renderer.initialize();
			
			//Set the initial State to the loading screen.
			GameState.active().activate();
			
			//Run this loop, until the user requests a close.
			while(!Display.isCloseRequested()){
				calcDelta();
				
				Controller.update();
				
				de.lns.tick.State.active().update();
				
				Renderer.tick();
				
				//Draw all rendered contents to the screen
				Display.update();
				//
				if(frameCap>0){
					Display.sync(frameCap);
				}
				if(Player.getInstance().getLife()<=0){
					return;
				}
			}
			
			//End the program normally.
			exit();
		} catch(Exception e) {
			kill("Uncaught Exception ocurred!", e);
		}
	}
	
	/**
	 * This method should be called if (and <b>ONLY</b> if) <br>
	 * an error has occurred in any part of the program. <br>
	 * It frees all resources occupied by this program, <br>
	 * prints the error message and the Exception's stack trace to System.err <br>
	 * and then exits the Java VM with an error.
	 * 
	 * @param errMsg - An additional error message.
	 * @param error - The Exception, that caused the error
	 */
	public static void kill(String errMsg, Exception error){
		System.err.println(errMsg);
		error.printStackTrace(System.err);
		dispose();
		System.exit(1);
	}
	
	private static void exit(){
		dispose();
		System.exit(0);
	}
	
	/**
	 * This method is used to free all resources, occupied by this program. <br>
	 * It is only called directly before calling System.exit().
	 */
	private static void dispose(){
		//TODO: Free all resources on call
		//Free all resources on the Graphics Adapter.
		Renderer.dispose();
	}
	
	
	/**
	 * Calculates the time, that has passed since the last Frame.
	 */
	private void calcDelta(){
		lastFrame = currentFrame;
		currentFrame = (Sys.getTime() * 1000)/Sys.getTimerResolution();
		delta = (int)(currentFrame - lastFrame);
	}
	
	//Getters and setters start here//
	/**
	 * @return The amount of milliseconds,
	 * that have elapsed since the last frame.
	 */
	public static int getDelta(){
		return delta;
	}
	
	public static long getTime(){
		return currentFrame;
	}
	
	public static int getFrameCap(){
		return frameCap;
	}
	
	public static void setFrameCap(int cap){
		if(cap>0){
			frameCap = cap;
		} else{
			frameCap = 0;
		}
	}
	
}
